package application.thread;

import application.ListOption;
import exception.UknownProtocolCodeException;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
import protocol.ProtocolCode;
import protocol.message.AccessGranted;
import protocol.message.AccountAccess;
import protocol.message.AccountRegister;
import protocol.message.RegisterGranted;
import stream.ByteCoding;

/**
 *
 * @author Simão
 */
public class ServerThread extends ProtocolThread {

    public ServerThread(Socket socket) {
        super(socket);
    }

    @Override
    public void run() {
        ReaderThread serverReader;
        WriterThread serverWriter;
        InputStream inputStream;
        DataInputStream dataInputStream;
        OutputStream outputStream;
        DataOutputStream dataOutputStream;
        AccountAccess accountAccess;
        AccountRegister accountRegister;
        ProtocolCode protocolCode;
        boolean accessOrRegisterEstabilished;
        byte[] stream;
        System.out.println("A new ServerThread has started.");
        try {
            outputStream = socket.getOutputStream();
            dataOutputStream = new DataOutputStream(outputStream);
            System.out.println("Socket DataOutputStream initialized.");
            inputStream = socket.getInputStream();
            dataInputStream = new DataInputStream(inputStream);
            System.out.println("Socket DataInputStream initialized.");
        } catch (IOException ex) {
            System.out.println("IOException in socket. Aborting connection.");
            try {
                socket.close();
            } catch (IOException ex1) {
                System.out.println("IOException while closing socket.");
            }
            return;
        }
        serverReader = new ReaderThread(dataInputStream, readerSemaphore, this);
        serverWriter = new WriterThread(dataOutputStream, writerSemaphore, this);
        serverReader.start();
        serverWriter.start();
        accessOrRegisterEstabilished = false;
        while (true) {
            try {
                System.out.println("Waiting for messages from ReaderThread.");
                readerSemaphore.acquire();
            } catch (InterruptedException ex) {
                System.out.println("InterruptedException in synchronization point. Aborting connection.");
                try {
                    socket.close();
                } catch (IOException ex1) {
                    System.out.println("IOException while closing socket.");
                }
                return;
            }
            stream = useReaderList(ListOption.REMOVE, null);
            System.out.println("Message obtained from ReaderList.");
            try {
                protocolCode = ByteCoding.setAsProtocolCode(stream);
            } catch (UknownProtocolCodeException ex) {
                System.out.println("Message type is invalid. Ignoring message.");
                continue;
            }
            System.out.println("Message type identified and valid.");
            switch (protocolCode) {
                case ACCOUNT_REGISTER:
                    accountRegister = new AccountRegister(stream);
                    System.out.println(accountRegister);
                    if (accessOrRegisterEstabilished) {
                        System.out.println("User requesting register while is already connected. Ignoring message.");
                        continue;
                    }
                    /* check if access is valid */
                    System.out.println("Connection access checked.");
                    useWriterList(
                            ListOption.ADD,
                            new RegisterGranted(false, (long) 999, accountRegister).getBytes());
                    System.out.println("Message to client added to WriterList.");
                    break;
                case ACCOUNT_ACCESS:
                    accountAccess = new AccountAccess(stream);
                    System.out.println(accountAccess);
                    if (accessOrRegisterEstabilished) {
                        System.out.println("User requesting access while is already connected. Ignoring message.");
                        continue;
                    }
                    /* check if register is valid */
                    System.out.println("Account register checked.");
                    useWriterList(
                            ListOption.ADD,
                            new AccessGranted(false, (long) 999, accountAccess).getBytes());
                    System.out.println("Message to client added to WriterList.");
                    break;
                /* process other protocol message types */
                default:
                    System.out.println("Message with unexcpected type received. Ignoring message.");
            }
        }
    }
}
